package de.slothsoft.commons.swing.editorview;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;

public class EditorViewPanel extends JPanel {

	private static final long serialVersionUID = 1L;

	private Map<Class<?>, Class<? extends Editor<?>>> registeredEditors = new HashMap<Class<?>, Class<? extends Editor<?>>>();
	private Map<Object, Editor<?>> openEditors = new HashMap<Object, Editor<?>>();

	private List<View> openViews = new ArrayList<View>();

	private JTabbedPane viewsPane;
	private JTabbedPane editorsPane;

	public EditorViewPanel() {
		createLayout();
	}

	private void createLayout() {

		this.viewsPane = new JTabbedPane();
		this.editorsPane = new JTabbedPane();

		JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
				this.viewsPane, this.editorsPane);
		splitPane.setOneTouchExpandable(true);
		splitPane.setDividerLocation(250);

		Dimension minimumSize = new Dimension(100, 200);
		this.viewsPane.setMinimumSize(minimumSize);
		this.editorsPane.setMinimumSize(minimumSize);

		setLayout(new BorderLayout());
		add(splitPane, BorderLayout.CENTER);
	}

	public <E> void registerEditor(Class<E> registeredClass,
			Class<? extends Editor<E>> editorClass) {
		this.registeredEditors.put(registeredClass, editorClass);
	}

	public <V extends View> V openView(Class<V> viewClass) {
		V view = findOpenView(viewClass);
		if (view != null) {
			selectPart(this.viewsPane, view);
			return view;
		}
		view = openPart(this.viewsPane, viewClass, true);
		view.init(this);
		updatePartTabFromPart(this.viewsPane, view);
		this.openViews.add(view);
		return view;
	}

	@SuppressWarnings("unchecked")
	public <V extends View> V findOpenView(Class<V> viewClass) {
		for (View view : this.openViews)
			if (view.getClass() == viewClass)
				return (V) view;
		return null;
	}

	@SuppressWarnings("unchecked")
	public <E> Editor<E> openEditor(E object) {
		Editor<E> editor = (Editor<E>) this.openEditors.get(object);
		if (editor != null)
			return selectPart(this.editorsPane, editor);
		return doOpenEditor(object);
	}

	private <E> Editor<E> doOpenEditor(E object) {
		@SuppressWarnings("unchecked")
		Class<? extends Editor<E>> editorClass = (Class<? extends Editor<E>>) this.registeredEditors
				.get(object.getClass());
		if (editorClass == null)
			throw new RuntimeException("No editor was registered for "
					+ object.getClass());
		Editor<E> editor = openPart(this.editorsPane, editorClass, false);
		editor.init(this, object);
		updatePartTabFromPart(this.editorsPane, editor);
		this.openEditors.put(object, editor);
		return editor;
	}

	private <P extends Part> P openPart(JTabbedPane tabbedPane,
			Class<P> partClass, boolean useTitle) {
		try {
			P part = partClass.newInstance();
			Component component = part.getComponent();
			tabbedPane.addTab(useTitle ? part.getTitle() : "", part.getIcon(),
					component, part.getTip());
			int index = tabbedPane.indexOfComponent(component);
			tabbedPane.setSelectedIndex(index);
			tabbedPane.setTabComponentAt(index, new PartTab(this));
			return part;
		} catch (Exception e) {
			throw new RuntimeException("Could not create part " + partClass, e);
		}
	}

	private void updatePartTabFromPart(JTabbedPane tabbedPane, Part part) {
		int index = tabbedPane.indexOfComponent(part.getComponent());
		if (index != -1) {
			PartTab tab = (PartTab) tabbedPane.getTabComponentAt(index);
			tab.updateControlsFromPart(part);
		}
	}

	public <P extends Part> P selectPart(JTabbedPane tabbedPane, P part) {
		int index = tabbedPane.indexOfComponent(part.getComponent());
		if (index != -1) {
			tabbedPane.setSelectedIndex(index);
		}
		return part;
	}

	public void closeEditor(Editor<?> editor) {
		// TODO: check for dirty and stuff
		closePart(this.editorsPane, editor);
		this.openEditors.remove(editor.getModel());
	}

	public void closeView(View view) {
		closePart(this.viewsPane, view);
		this.openViews.remove(view);
	}

	private void closePart(JTabbedPane tabbedPane, Part part) {
		int index = tabbedPane.indexOfComponent(part.getComponent());
		if (index != -1) {
			tabbedPane.remove(index);
		}
	}

	public Editor<?> getActiveEditor() {
		return (Editor<?>) getActivePart(this.editorsPane);
	}

	public View getActiveView() {
		return (View) getActivePart(this.viewsPane);
	}

	public Part getActivePart(JTabbedPane tabbedPane) {
		return (Part) tabbedPane.getSelectedComponent();
	}

	public Map<Object, Editor<?>> getOpenEditors() {
		return this.openEditors;
	}

	public List<View> getOpenViews() {
		return this.openViews;
	}

}
